package services

import (
    "context"
    "encoding/json"
    "fmt"
    "time"

    "github.com/google/uuid"
    "github.com/redis/go-redis/v9"

    "github.com/samaa/photosalesplus/backend/internal/config"
    "github.com/samaa/photosalesplus/backend/internal/repositories"
)

type PaymentChannel string

const (
    ChannelWeChat PaymentChannel = "wechat"
    ChannelAlipay PaymentChannel = "alipay"
)

type PaymentStatus string

const (
    StatusPending  PaymentStatus = "pending"
    StatusPaid     PaymentStatus = "paid"
    StatusFailed   PaymentStatus = "failed"
)

type PaymentSession struct {
    ID        string         `json:"id"`
    OrderID   uint           `json:"order_id"`
    Channel   PaymentChannel `json:"channel"`
    Status    PaymentStatus  `json:"status"`
    CreatedAt time.Time      `json:"created_at"`
}

type PaymentService interface {
    CreateSession(ctx context.Context, orderID uint, channel PaymentChannel) (*PaymentSession, string, error)
    GetSession(ctx context.Context, id string) (*PaymentSession, error)
    MarkPaid(ctx context.Context, id string) error
    MarkFailed(ctx context.Context, id string) error
}

type paymentService struct {
    cfg    *config.Config
    redis  *redis.Client
    orders repositories.OrderRepository
}

func NewPaymentService(cfg *config.Config, r *redis.Client, orders repositories.OrderRepository) PaymentService {
    return &paymentService{cfg: cfg, redis: r, orders: orders}
}

func (s *paymentService) key(id string) string { return "payments:sessions:" + id }

func (s *paymentService) CreateSession(ctx context.Context, orderID uint, channel PaymentChannel) (*PaymentSession, string, error) {
    id := uuid.NewString()
    sess := &PaymentSession{ID: id, OrderID: orderID, Channel: channel, Status: StatusPending, CreatedAt: time.Now()}
    b, _ := json.Marshal(sess)
    if err := s.redis.Set(ctx, s.key(id), b, 2*time.Hour).Err(); err != nil {
        return nil, "", err
    }
    appID := ""
    switch channel {
    case ChannelWeChat:
        appID = s.cfg.WechatAppID
    case ChannelAlipay:
        appID = s.cfg.AlipayAppID
    }
    qrcode := fmt.Sprintf("PAY://%s/%s?order=%d&app_id=%s", channel, id, orderID, appID)
    return sess, qrcode, nil
}

func (s *paymentService) GetSession(ctx context.Context, id string) (*PaymentSession, error) {
    v, err := s.redis.Get(ctx, s.key(id)).Bytes()
    if err != nil { return nil, err }
    var sess PaymentSession
    if err := json.Unmarshal(v, &sess); err != nil { return nil, err }
    return &sess, nil
}

func (s *paymentService) setStatus(ctx context.Context, id string, status PaymentStatus) error {
    sess, err := s.GetSession(ctx, id)
    if err != nil { return err }
    sess.Status = status
    b, _ := json.Marshal(sess)
    if err := s.redis.Set(ctx, s.key(id), b, 2*time.Hour).Err(); err != nil { return err }
    if status == StatusPaid {
        _ = s.orders.UpdateStatus(ctx, sess.OrderID, "completed")
    } else if status == StatusFailed {
        _ = s.orders.UpdateStatus(ctx, sess.OrderID, "canceled")
    }
    return nil
}

func (s *paymentService) MarkPaid(ctx context.Context, id string) error  { return s.setStatus(ctx, id, StatusPaid) }
func (s *paymentService) MarkFailed(ctx context.Context, id string) error { return s.setStatus(ctx, id, StatusFailed) }

